package org.jasig.cas.ticket.registry;

import com.sun.istack.NotNull;
import org.jasig.cas.ticket.ServiceTicket;
import org.jasig.cas.ticket.Ticket;
import org.jasig.cas.ticket.TicketGrantingTicket;

import javax.validation.constraints.Min;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.TimeUnit;

public class RedisTicketRegistry extends AbstractDistributedTicketRegistry{
    @NotNull
    private final TicketRedisTemplate reidsTemplate;

    /**
     * TGT cache entry timeout in seconds.
     */
    @Min(0)
    private final int tgtTimeout;

    /**
     * ST cache entry timeout in seconds.
     */
    @Min(0)
    private final int stTimeout;

    private final static String TICKET_PREFIX = "TICKETGRANTINGTICKET:";

    public RedisTicketRegistry(TicketRedisTemplate reidsTemplate,int tgtTimeout,int stTimeout){
        this.reidsTemplate=reidsTemplate;
        this.tgtTimeout=tgtTimeout;
        this.stTimeout=stTimeout;
    }
    @Override
    public void addTicket(Ticket ticket) {
        logger.debug("Adding ticket {}", ticket);
        try {
            reidsTemplate.opsForValue().set(TICKET_PREFIX+ticket.getId(), ticket, getTimeout(ticket), TimeUnit.SECONDS);
        } catch (final Exception e) {
            logger.error("Failed adding {}", ticket, e);
        }
    }

    @Override
    public Ticket getTicket(String ticketId) {
        try {
            final Ticket t = (Ticket) this.reidsTemplate.opsForValue().get(TICKET_PREFIX+ticketId);
            if (t != null) {
                return getProxiedTicketInstance(t);
            }
        } catch (final Exception e) {
            logger.error("Failed fetching {} ", ticketId, e);
        }
        return null;
    }

    @Override
    public boolean deleteTicket(String ticketId) {
        logger.debug("Deleting ticket {}", ticketId);
        try {
            this.reidsTemplate.delete(TICKET_PREFIX+ticketId);
            return true;
        } catch (final Exception e) {
            logger.error("Failed deleting {}", ticketId, e);
        }
        return false;
    }

    @Override
    public Collection<Ticket> getTickets() {
        Set<Ticket> tickets = new HashSet<Ticket>();
        Set<String> keys = this.reidsTemplate.keys(TICKET_PREFIX + "*");
        for (String key:keys){
            Ticket ticket = (Ticket)this.reidsTemplate.opsForValue().get(key);
            if(ticket==null){
                this.reidsTemplate.delete(TICKET_PREFIX+key);
            }else{
                tickets.add(ticket);
            }
        }
        return tickets;
    }

    @Override
    protected void updateTicket(Ticket ticket) {
        logger.debug("Updating ticket {}", ticket);
        try {
            this.reidsTemplate.delete(TICKET_PREFIX+ticket.getId());
            reidsTemplate.opsForValue().set(TICKET_PREFIX+ticket.getId(),ticket, getTimeout(ticket), TimeUnit.SECONDS);
        } catch (final Exception e) {
            logger.error("Failed updating {}", ticket, e);
        }
    }

    @Override
    protected boolean needsCallback() {
        // TODO Auto-generated method stub
        return true;
    }
    private int getTimeout(final Ticket t) {
        if (t instanceof TicketGrantingTicket) {
            return this.tgtTimeout;
        } else if (t instanceof ServiceTicket) {
            return this.stTimeout;
        }
        throw new IllegalArgumentException("Invalid ticket type");
    }
}